﻿using System;
using System.Linq;
using ActiveUp.Net.Mail;
using CounterSoft.Gemini.WebServices;
using CounterSoft.Gemini.Commons;
using CounterSoft.Gemini.Commons.Rest;
using CounterSoft.Gemini.Commons.Entity;
using System.Configuration;
using System.Collections.Generic;
using System.IO;
using System.Web;
using System.Collections;

namespace Gemama
{
    public class Engine
    {
        private static ProjectEN[] projects;

        private static UserEN[] users;

        private static IssueEN[] issues;

        private static ServiceManager serviceManager;

        private static Engineresult res;

        private static List<IssueEN> projectTable = new List<IssueEN>();

        private static List<IssueEN> assigneeTable = new List<IssueEN>();

        public Engineresult Reminder()
        {
            res = new Engineresult();
            res.ResultCode = 0;
            res.ResultString = string.Empty;

            try
            {
                serviceManager = this.GeminiInitialize();
                IssuesFilterEN filter = new IssuesFilterEN();

                IssuesService issueService = serviceManager.IssuesService;

                projects = serviceManager.ProjectsService.GetProjects();

                ProjectsService projectservice = serviceManager.ProjectsService;
                foreach (ProjectEN project in projects)
                {
                    ProjectAttributeEN[] projectAttributes = projectservice.GetProjectAttributes(project.ProjectID);
                    var found = from pa in projectAttributes
                                where pa.AttributeName == ConfigurationSettings.AppSettings["SummaryProjectSwitchName"]
                                select pa;
                    if (found.Count() > 0) // send only to projects having a speciel projectattribute
                    {
                        IssuesFilterEN issueFilter = new IssuesFilterEN();
                        issueFilter.ProjectID = project.ProjectID.ToString();
                        issueFilter.ExcludeClosed = true;
                        issueFilter.UserID = serviceManager.UsersService.WhoAmI().UserID;
                        issues = issueService.GetFilteredIssues(issueFilter);
                        foreach (IssueEN issue in issues)
                        {
                            if (issue.DueDateString != "01/01/0001" && issue.DueDateString != "1/1/0001")
                            {
                                if (issue.DaysLeft.Substring(0, 1) == "+")
                                {
                                    if (issue.AssignedTo.Length > 0)
                                    {
                                        assigneeTable.Add(issue);
                                    }

                                    projectTable.Add(issue);
                                }
                            }
                        }
                    }
                }

                this.CreateAssigneeMail();
                this.CreateProjectMail();
            }
            catch (Exception excep)
            {
                res.ResultCode = 200;
                res.ResultString = excep.Message;
            }

            return res;
        }

        private void CreateProjectMail()
        {
            string filePath = System.Web.HttpContext.Current.Request.PhysicalApplicationPath + "TemplateProjectReminder.htm";
            StreamReader streamReader = new StreamReader(filePath, System.Text.Encoding.UTF8);
            string mailOrg = streamReader.ReadToEnd();
            streamReader.Close();

            int tbodyStart = mailOrg.IndexOf("<tbody>");
            int tbodyEnd = mailOrg.IndexOf("</tbody>");
            string tbodyStringOrg = mailOrg.Substring(tbodyStart, tbodyEnd - tbodyStart + 8);
            string tbodyStringNew = string.Empty;
            IEnumerable<IssueEN> query = from i in projectTable orderby i.ProjectID select i;

            int projectID = 0;
            string mail = mailOrg;
            foreach (IssueEN issue in query)
            {
                if (projectID > 0 && projectID != issue.ProjectID)
                {
                    mail = mail.Replace(tbodyStringOrg, tbodyStringNew);
                    mail = mail.Replace("${ProjectLeadName}", serviceManager.ProjectsService.GetProject(projectID).ProjectLeadName);
                    mail = mail.Replace("${ProjectName}", serviceManager.ProjectsService.GetProject(projectID).ProjectName);
                    this.SendMail(serviceManager.UsersService.GetUser(serviceManager.ProjectsService.GetProject(projectID).UserID).EmailAddress, mail);
                    res.ReminderProjectMail++;
                    mail = mailOrg;
                    tbodyStringNew = string.Empty;
                }

                projectID = issue.ProjectID;
                tbodyStringNew += tbodyStringOrg;
                tbodyStringNew = tbodyStringNew.Replace("${IssueID}", issue.IssueID.ToString());
                tbodyStringNew = tbodyStringNew.Replace("${IssueSummary}", issue.IssueSummary);
                tbodyStringNew = tbodyStringNew.Replace("${DueDateString}", issue.DueDateString);
                tbodyStringNew = tbodyStringNew.Replace("${IssueStatusDesc}", issue.IssueStatusDesc);
            }

            if (projectID > 0)
            {
                mail = mail.Replace(tbodyStringOrg, tbodyStringNew);
                mail = mail.Replace("${ProjectLeadName}", serviceManager.ProjectsService.GetProject(projectID).ProjectLeadName);
                mail = mail.Replace("${ProjectName}", serviceManager.ProjectsService.GetProject(projectID).ProjectName);
                this.SendMail(serviceManager.UsersService.GetUser(serviceManager.ProjectsService.GetProject(projectID).UserID).EmailAddress, mail);
                res.ReminderProjectMail++;
            }
        }

        private void CreateAssigneeMail()
        {
            string filePath = System.Web.HttpContext.Current.Request.PhysicalApplicationPath + "TemplateAssigneeReminder.htm";
            StreamReader streamReader = new StreamReader(filePath, System.Text.Encoding.UTF8);
            string mailOrg = streamReader.ReadToEnd();
            streamReader.Close();

            int tbodyStart = mailOrg.IndexOf("<tbody>");
            int tbodyEnd = mailOrg.IndexOf("</tbody>");
            string tbodyStringOrg = mailOrg.Substring(tbodyStart, tbodyEnd - tbodyStart + 8);
            string tbodyStringNew = string.Empty;
            IEnumerable<IssueEN> query = from i in assigneeTable orderby i.AssignedTo select i;

            string assignedTo = string.Empty;
            string mail = mailOrg;
            foreach (IssueEN issue in query)
            {
                if (assignedTo.Length > 0 && assignedTo != issue.AssignedTo)
                {
                    mail = mail.Replace(tbodyStringOrg, tbodyStringNew);
                    mail = mail.Replace("${AssigneeName}", serviceManager.UsersService.GetUser(int.Parse(assignedTo)).Firstname);
                    this.SendMail(serviceManager.UsersService.GetUser(int.Parse(assignedTo)).EmailAddress, mail);
                    res.ReminderAssigneeMail++;
                    mail = mailOrg;
                    tbodyStringNew = string.Empty;
                }

                assignedTo = issue.AssignedTo;
                tbodyStringNew += tbodyStringOrg;
                tbodyStringNew = tbodyStringNew.Replace("${IssueID}", issue.IssueID.ToString());
                tbodyStringNew = tbodyStringNew.Replace("${IssueSummary}", issue.IssueSummary);
                tbodyStringNew = tbodyStringNew.Replace("${DueDateString}", issue.DueDateString);
                tbodyStringNew = tbodyStringNew.Replace("${IssueStatusDesc}", issue.IssueStatusDesc);
                tbodyStringNew = tbodyStringNew.Replace("${ProjectName}", issue.ProjectName);
            }

            if (assignedTo.Length > 0)
            {
                mail = mail.Replace(tbodyStringOrg, tbodyStringNew);
                mail = mail.Replace("${AssigneeName}", serviceManager.UsersService.GetUser(int.Parse(assignedTo)).Firstname);
                this.SendMail(serviceManager.UsersService.GetUser(int.Parse(assignedTo)).EmailAddress, mail);
                res.ReminderAssigneeMail++;
            }
        }

        private void SendMail(string emailAddress, string mail)
        {
            if (!string.IsNullOrEmpty(ConfigurationSettings.AppSettings["SummaryTestEmail"]))
            {
                emailAddress = ConfigurationSettings.AppSettings["SummaryTestEmail"];
            }

            int titleStart = mail.IndexOf("<title>");
            int titleEnd = mail.IndexOf("</title>");
            string titleString = mail.Substring(titleStart + 7, titleEnd - titleStart - 7);

            System.Net.Mail.SmtpClient client = new System.Net.Mail.SmtpClient();
            System.Net.Mail.MailMessage message = new System.Net.Mail.MailMessage();
            message.Body = mail;
            message.IsBodyHtml = true;
            message.From = new System.Net.Mail.MailAddress(ConfigurationSettings.AppSettings["SummaryFromEmail"]);
            message.ReplyTo = new System.Net.Mail.MailAddress(ConfigurationSettings.AppSettings["SummaryReplytoEmail"]);
            message.To.Add(emailAddress);
            message.Subject = titleString;
            client.Send(message);
        }

        public Engineresult Compute()
        {
            res = new Engineresult();
            res.ResultCode = 0;
            res.ResultString = string.Empty;

            try
            {
                serviceManager = this.GeminiInitialize();
                projects = serviceManager.ProjectsService.GetProjects();
                users = serviceManager.UsersService.GetUsers(GeminiConstant.EntityLoadPattern.Full);

                this.FetchNewIssues();
                this.ParseComments();
            }
            catch (Exception excep)
            {
                res.ResultCode = 200;
                res.ResultString = excep.Message;
            }

            return res;
        }

        private void FetchNewIssues()
        {
            Pop3Client pop = new Pop3Client();
            pop.Connect(ConfigurationSettings.AppSettings["NewIssuesServer"], ConfigurationSettings.AppSettings["NewIssuesUser"], ConfigurationSettings.AppSettings["NewIssuesPassword"]);
            int count = pop.MessageCount;

            for (int i = 1; i <= count; i++)
            {
                ActiveUp.Net.Mail.Message message = new Message();
                try
                {
                    message = pop.RetrieveMessageObject(i);
                }
                catch (Exception)
                {
                }
                if (message.From != null)
                {
                    ProjectEN project = GetProject(message.To);
                    if (project == null)
                    {
                        project = GetProject(message.Cc);
                    }
                    UserEN reportingUser = GetUser(message.From.Email);
                    if (project != null)
                    {
                        if (reportingUser == null)
                        {
                            reportingUser = GetUser(ConfigurationSettings.AppSettings["DefaultReportingUser"]);
                        }
                        string body = message.BodyText.TextStripped.Replace("\r\n", "<br />");
                        IssueEN issue = new IssueEN();

                        issue.VisibilityMemberType = CounterSoft.Gemini.Commons.GeminiConstant.SecurityMemberType.GlobalGroup;
                        issue.RiskLevel = GetDefaultValue(project.ProjectID, GeminiConstant.IssueAttribute.RiskLevel);
                        issue.ProjectID = project.ProjectID;
                        issue.IssueSummary = message.Subject;
                        issue.IssueLongDesc = body;
                        issue.IssueType = GetDefaultValue(project.ProjectID, GeminiConstant.IssueAttribute.IssueType);
                        issue.ReportedBy = reportingUser.UserID;
                        issue.IssueStatus = GetDefaultValue(project.ProjectID, GeminiConstant.IssueAttribute.IssueStatus);
                        issue.IssuePriority = GetDefaultValue(project.ProjectID, GeminiConstant.IssueAttribute.IssuePriority);
                        issue.IssueSeverity = GetDefaultValue(project.ProjectID, GeminiConstant.IssueAttribute.IssueSeverity);

                        IssueEN ni = serviceManager.IssuesService.CreateIssue(issue);

                        for (int ii = 0; ii < message.Attachments.Count; ii++)
                        {
                            FileEN file = new FileEN();
                            file.FileData = message.Attachments[ii].BinaryContent;
                            file.FileName = message.Attachments[ii].Filename;
                            file.ContentLength = message.Attachments[ii].BinaryContent.Length;
                            file.ProjectID = (int)project.ProjectID;
                            file.CommentID = null;
                            file.ContentType = message.Attachments[ii].ContentType.Type;
                            file.IssueID = ni.IssueID;
                            serviceManager.IssuesService.CreateAttachment(ni.IssueID, file);
                        }

                        for (int ii = 0; ii < message.EmbeddedObjects.Count; ii++)
                        {
                            FileEN file = new FileEN();
                            file.FileData = message.EmbeddedObjects[ii].BinaryContent;
                            file.FileName = message.EmbeddedObjects[ii].Filename;
                            file.ContentLength = message.EmbeddedObjects[ii].BinaryContent.Length;
                            file.ProjectID = (int)project.ProjectID;
                            file.CommentID = null;
                            file.ContentType = message.EmbeddedObjects[ii].ContentType.Type;
                            file.IssueID = ni.IssueID;
                            serviceManager.IssuesService.CreateAttachment(ni.IssueID, file);
                        }

                        for (int ii = 0; ii < message.SubMessages.Count; ii++)
                        {
                            FileEN file = new FileEN();
                            file.FileData = StrToByteArray(message.SubMessages[ii].BodyText.TextStripped);
                            file.FileName = message.SubMessages[ii].Subject + ".txt";
                            file.ContentLength = file.FileData.Length;
                            file.ProjectID = (int)project.ProjectID;
                            file.CommentID = null;
                            file.ContentType = message.SubMessages[ii].ContentType.Type;
                            file.IssueID = ni.IssueID;
                            serviceManager.IssuesService.CreateAttachment(ni.IssueID, file);
                        }

                        IssueCommentEN issueComment = new IssueCommentEN();
                        issueComment.Comment = ConfigurationSettings.AppSettings["CommentNote"].Replace("%email", message.From.Email).Replace("%name", message.From.Name);
                        issueComment.IssueID = ni.IssueID;
                        issueComment.ProjectID = project.ProjectID;
                        serviceManager.IssuesService.CreateComment(ni.IssueID, issueComment);

                        res.IssuesOK++;
                        pop.DeleteMessage(i);
                    }
                    else
                    {
                        res.IssuesError++;
                    }
                }

            }

            pop.Disconnect();
        }

        private static int GetDefaultValue(int projectID, GeminiConstant.IssueAttribute issueAttribute)
        {
            ProjectDefaultValueEN[] projectdefaultvalues = serviceManager.ProjectsService.GetProjectDefaultValues(projectID);
            var tmp = from pdv in projectdefaultvalues
                      where pdv.FieldID == issueAttribute
                      select pdv;
            return int.Parse(tmp.First().DefaultValue);
        }

        private static string ExtractProjectcode(string email)
        {
            string res = email.Substring(0, email.IndexOf("@")).ToLower();
            string[] aliases = ConfigurationSettings.AppSettings["Aliases"].ToLower().Split("/".ToCharArray());
            foreach (string alias in aliases)
            {
                int split = alias.IndexOf("!");
                if (res == alias.Substring(0, split))
                {
                    res = alias.Substring(split + 1);
                    break;
                }
            }

            return res;
        }

        private void ParseComments()
        {
            Pop3Client pop = new Pop3Client();
            pop.Connect(ConfigurationSettings.AppSettings["CommentsServer"], ConfigurationSettings.AppSettings["CommentsUser"], ConfigurationSettings.AppSettings["CommentsPassword"]);
            int count = pop.MessageCount;

            for (int i = 1; i <= count; i++)
            {
                ActiveUp.Net.Mail.Message message = pop.RetrieveMessageObject(i);
                int issueID = this.IssueNumber(message.Subject);
                if (issueID > 0)
                {
                    IssueEN issue = null;
                    UserEN reportingUser = null;
                    try
                    {
                        issue = serviceManager.IssuesService.GetIssue(issueID);
                        reportingUser = GetUser(message.From.Email);
                    }
                    catch (Exception)
                    {
                        // ignore - handled in the following if-statement
                    }

                    if (issue != null && reportingUser != null)
                    {
                        IssueCommentEN comment = new IssueCommentEN();
                        comment.IssueID = issueID;
                        comment.Comment = message.BodyText.TextStripped.Replace("\r\n", "<br />");

                        comment.ProjectID = issue.ProjectID;
                        comment.UserID = reportingUser.UserID;

                        for (int ii = 0; ii < message.Attachments.Count; ii++)
                        {
                            FileEN file = new FileEN();
                            file.FileData = message.Attachments[ii].BinaryContent;
                            file.FileName = message.Attachments[ii].Filename;
                            file.ContentLength = message.Attachments[ii].BinaryContent.Length;
                            file.ProjectID = comment.ProjectID;
                            file.CommentID = null;
                            file.ContentType = message.Attachments[ii].ContentType.Type;
                            file.IssueID = issueID;
                            serviceManager.IssuesService.CreateAttachment(issueID, file);
                        }

                        for (int ii = 0; ii < message.EmbeddedObjects.Count; ii++)
                        {
                            FileEN file = new FileEN();
                            file.FileData = message.EmbeddedObjects[ii].BinaryContent;
                            file.FileName = message.EmbeddedObjects[ii].Filename;
                            file.ContentLength = message.EmbeddedObjects[ii].BinaryContent.Length;
                            file.ProjectID = comment.ProjectID;
                            file.CommentID = null;
                            file.ContentType = message.EmbeddedObjects[ii].ContentType.Type;
                            file.IssueID = issueID;
                            serviceManager.IssuesService.CreateAttachment(issueID, file);
                        }

                        for (int ii = 0; ii < message.SubMessages.Count; ii++)
                        {
                            FileEN file = new FileEN();
                            file.FileData = StrToByteArray(message.SubMessages[ii].BodyText.TextStripped);
                            file.FileName = message.SubMessages[ii].Subject + ".txt";
                            file.ContentLength = file.FileData.Length;
                            file.ProjectID = comment.ProjectID;
                            file.CommentID = null;
                            file.ContentType = message.SubMessages[ii].ContentType.Type;
                            file.IssueID = issueID;
                            serviceManager.IssuesService.CreateAttachment(issueID, file);
                        }

                        serviceManager.IssuesService.CreateComment(issueID, comment);

                        res.CommentsOK++;
                        pop.DeleteMessage(i);
                    }
                    else
                    {
                        res.CommentsError++;
                    }
                }
                else
                {
                    res.CommentsError++;
                }
            }

            pop.Disconnect();
        }

        private int IssueNumber(string p)
        {
            int res = 0;
            p = "XXX" + p; // to avoid confusing regarding location at 0
            int a = p.IndexOf("[");
            int b = p.IndexOf("]");
            if (a > 0 && b > a)
            {
                p = p.Substring(a + 1, b - a - 1);
                int c = p.IndexOf("-");
                int.TryParse(p.Substring(c + 1), out res);
            }

            return res;
        }

        private ServiceManager GeminiInitialize()
        {
            ServiceManager serviceManager = null;
            try
            {
                serviceManager = new ServiceManager(
                    ConfigurationSettings.AppSettings["GeminiServerURL"],
                    ConfigurationSettings.AppSettings["GeminiUser"],
                   ConfigurationSettings.AppSettings["GeminiPassword"],
                   ConfigurationSettings.AppSettings["GeminiAPIkey"],
                    bool.Parse(ConfigurationSettings.AppSettings["GeminiWindowsAuthenticationMode"]));
            }
            catch (GeminiRestException ex1)
            {
                Console.WriteLine(ex1.Message);
            }
            catch (Exception ex2)
            {
                Console.WriteLine(ex2.Message);
            }

            return serviceManager;
        }

        private static UserEN GetUser(string email)
        {
            var found = from u in users
                        where u.EmailAddress.ToLower() == email.ToLower()
                        select u;
            if (found.Count() == 0)
            {
                return null;
            }
            else
            {
                return found.First();
            }
        }

        private static UserEN GetUser(int userID)
        {
            var found = from u in users
                        where u.UserID == userID
                        select u;
            if (found.Count() == 0)
            {
                return null;
            }
            else
            {
                return found.First();
            }
        }

        //private static ProjectEN GetProject(string projectcode)
        //{
        //    var found = from o in projects
        //                where o.ProjectCode.ToLower() == projectcode.ToLower()
        //                select o;
        //    if (found.Count() == 0)
        //    {
        //        return null;
        //    }
        //    else
        //    {
        //        return found.First();
        //    }
        //}

        private static ProjectEN GetProject(AddressCollection emails)
        {
            ProjectEN res = new ProjectEN();

            res = null;

            foreach (Address email in emails)
            {
                string projectcode = ExtractProjectcode(email.Email.ToString());
                var found = from o in projects
                            where o.ProjectCode.ToLower() == projectcode.ToLower()
                            select o;
                if (found.Count() > 0)
                {
                    res = found.First();
                    break;
                }
            }
            return res;
        }

        public static byte[] StrToByteArray(string str)
        {
            System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
            return encoding.GetBytes(str);
        }
    }
}
